/* This is derived from Ccallingorder.c, except that instead of placing
   blocks in their calling order, functions are expanded in-line. This
   is done only once (to prevent code size blowing out)  */

#include <stdio.h>
#include <ctype.h>

#define  MAXWORDS  5000		/* max no-punctuation tokens in a list program */
#define  MAXCHARS  25		/* max characters per token */
#define  MAXPROCS  100		/* room for MAXPROCS functions to be defined */

/* #define DEBUG 1 */


typedef  enum boolean {FALSE, TRUE}  boolean;
typedef  char  string[MAXCHARS];

typedef struct  funstruct
    {
    string  name;
    int  start, finish;     /* start and finish positions in prog_list for fun */
    boolean expanded;
}  funstruct;


/* global variables  */

string  prog_list[MAXWORDS];		/* program token list */
funstruct  proclist[MAXPROCS];	/* array of function structures, 1 per function */

int  nfuns,			/* Number of functions defined */
     pix,			/* Program index - index of last full cell */
     offset, incr;		/* Used to distance functions from different questions */

char *whoami;		/* name of binary; used for error messages */


int  read_prog(startword)
char *startword;
{
    register  int  brackets ;
    register  string *listpt;
    boolean  inadef, nextisdefname;
    int  startix;

    inadef = nextisdefname = FALSE;
    pix = nfuns =  -1;
    brackets = 0;
    startix = -1;
    listpt = (string *) prog_list[0];

    while ( scanf("%s", listpt) != EOF)
	{
	if ( **listpt == '{' )
	    brackets++;
	else if ( **listpt == '}' )
	    {
	    if ( --brackets == 0 && inadef)
		{
		proclist[nfuns].finish = pix ;
		inadef = FALSE;
		}
	    }
	else if ( strcmp(*listpt, "DEFUN") == 0)
	    {
	    inadef = nextisdefname = TRUE;
	    listpt++;
	    pix++;
	    }
	else if ( nextisdefname )  /* this should be the actual name */
	    {
	    nextisdefname = FALSE;
	    if ( ++ nfuns == MAXPROCS )
		{
		fprintf(stderr, "More functions defined that there is room to store. One not to fit is %s\n", *listpt);
		exit(0);   
		//bery:  exit byl bez arg.
		}

	    strcpy(proclist[nfuns].name, *listpt);
	    proclist[nfuns].start = pix + 1;
	    proclist[nfuns].expanded = FALSE;
	    if (strcmp(*listpt, startword) == 0 )
		startix = nfuns;
	    listpt++;
	    pix++;
	    }
	else
	    {
	    listpt++;
	    pix++;
	    }
	if(pix == MAXWORDS)  /* WOOPS - too few tokens allowed recompile! */
	    {
	    fprintf(stderr,"Too few tokens allowed for the source program\n");
	    fprintf(stderr,"Recompile, increasing MAXWORDS\n");
	    exit(1);
	    }
	}
    
    if(inadef)    /* program not properly terminated. Make do! */
	proclist[nfuns].finish = pix;

    return(startix);	/* return proclist index of top-level-function */
}	/*  read_prog  */


void printprog()
{
    register int i;

    printf("Listing of program tokens:\n\n");
    for (i = 0; i <= pix; i++)
	printf("%d: %s\n", i, prog_list[i]);
    printf("\nList of functions\n");
    for (i = 0; i <= nfuns; i++)
	printf("Name: %s, Start: %d, Finish: %d\n",
	    proclist[i].name, proclist[i].start, proclist[i].finish);
}

/* scan the function descriptors to see if a given string matches a function 
   name  */

int  scanprocs(ident)
char  *ident;
{
    register  int  i;

    for (i = 0; i <= nfuns; i++)
	if (strcmp(ident, proclist[i].name) == 0)
	    return(i);
    return(-1);
}



/* scan all the tokens between start and finish to see if any is a function call
   to a user defined function, i.e. something that has an entry in the proc_list
   If a token is a function name AND the function is not already expanded
   mark it and then traverse the function's code  */

void  expandblock(start, finish)
int  start, finish;
{
    register  int  i, lix;

    printf("C_DEFUN\n");
    for (i = start; i <= finish; i++)
	{
	if ((lix = scanprocs(prog_list[i])) > -1 )      /* Function found */
	    {
	    if ( ! proclist[lix].expanded && i != start)   /* Don't expand header ! */
		{

#ifdef DEBUG
		printf("\nExpanding block %d\n",lix);
#endif
		proclist[lix].expanded = TRUE;
		expandblock(proclist[lix].start, proclist[lix].finish);
		}
	    else
		printf("_FUN%d_\n", offset + lix);
	    }
	else
	    printf("%s\n", prog_list[i]);
	}

#ifdef DEBUG
    printf("END_DEFUN\n");
#endif

}	/* expandblock */


char *translate(char *str1, char *str2)
{
    register char *ch1 = str1, *ch2 = str2;

    for( ; *ch1 != '\0'; ch1++, ch2++)
	*ch2 = isupper(*ch1) ? tolower(*ch1) : *ch1;

    *ch2 = '\0';
    return(str2);
}


main(argc, argv)
register int  argc;
register char **argv;
{
    register int i;
    char *start_fun;
    int lix;
    string str1;

    if (argc == 1)
	{
	fprintf(stderr, "Usage: %s <function-name> ......\n",argv[0]);
	exit(1);
	//bery: exit byl bez arg.
	}
    whoami = argv[0];
    offset = incr  = 0;
    lix = read_prog(translate(argv[1], str1));

#ifdef DEBUG
    printprog(); 
    printf("Expanding block %d\n",lix);
#endif

    proclist[lix].expanded = TRUE;
    expandblock(proclist[lix].start, proclist[lix].finish);
    for (i = 2; i < argc; i++)
	{
	offset = ++incr * 100;
	if ((lix = scanprocs(translate(argv[i], str1))) == -1)
	    fprintf(stderr, "%s: top-level function name %s is not known\n",whoami,argv[i]);
	else
	    {
	    proclist[lix].expanded = TRUE;
	    expandblock(proclist[lix].start, proclist[lix].finish);
	    }
	}
}
